package com.runa.protocol.analysis;

import com.runa.protocol.dto.COTPSectionData;
import com.runa.protocol.dto.S7DataItem;
import com.runa.protocol.dto.S7ResponseData;
import com.runa.protocol.enums.ByteOrder;
import com.runa.protocol.enums.COTPType;
import com.runa.protocol.enums.S7Enum;
import com.runa.protocol.enums.S7Error;
import com.runa.protocol.util.ByteUtil;

public class S7MessageAnalysis extends MetaBufferAnalysis<byte[]> {

    @Override
    public int fill(byte[] data, int size,String netEquNo) {
        responseData = null;
        if (size < 4) return -1;
        int begin;
        for (begin = 0; begin < size; begin++) {
            if (data[begin] == 0x03 && data[begin+1] == 0) break;
        }
        if (size < begin + 4) return -1;
        int index = begin + 2;
        int tpktSize = ByteUtil.byteToUShort(data, index, ByteOrder.AB);
        index += 2;
        if (size < tpktSize) return -1;

        int cotpSize = data[index++];
        COTPType cotpType = COTPType.valueOf(data[index++]);
        if (cotpType != null) {
            switch (cotpType) {
                case ConnectReq:
                    break;
                case ConnectConfirm:
                    COTPSectionData cotpSectionData = new COTPSectionData(cotpType);
                    cotpSectionData.setDstRef((data[index++]<<8) | data[index++]);
                    cotpSectionData.setSrcRef((data[index++]<<8) | data[index++]);
                    cotpSectionData.setOption(data[index]);
                    //TODO...
                    this.responseData = new S7ResponseData(cotpSectionData);
                    break;
                case DataTransport:
                    cotpSectionData = new COTPSectionData(cotpType);
                    byte b = data[index++];
                    cotpSectionData.setLast((b & 0x80) == 0x80);
                    cotpSectionData.setTpdNum(b & 0x7f);
                    S7ResponseData responseData = new S7ResponseData(cotpSectionData);
                    if (data[index++] == 0x32) {
                        responseData.setPduType(S7Enum.PDUType.valueOf(data[index++]));
                        index += 2;
                        responseData.setPduRef(ByteUtil.byteToShort(data, index, ByteOrder.AB));
                        index += 2;
                        responseData.setParamLen(ByteUtil.byteToUShort(data, index, ByteOrder.AB));
                        index += 2;
                        responseData.setDataLen(ByteUtil.byteToUShort(data, index, ByteOrder.AB));
                        index += 2;
                        assert responseData.getPduType() != null;
                        switch (responseData.getPduType()) {
                            case ACK:
                                break;
                            case ACK_DATA:
                                responseData.setErrorClass(S7Error.S7ErrorClass.valueOf(data[index++]));
                                responseData.setErrorCode(S7Error.S7ErrorCode.valueOf(data[index++]));
                                if (responseData.getErrorClass() == S7Error.S7ErrorClass.NO_ERROR
                                        && responseData.getErrorCode() == S7Error.S7ErrorCode.NO_ERROR) {
                                    responseData.setFunCode(S7Enum.FunCode.valueOf(data[index++]));
                                    switch (responseData.getFunCode()) {
                                        case COMM_SET:
                                            index++;
                                            responseData.setMaxCaller(ByteUtil.byteToUShort(data, index, ByteOrder.AB));
                                            index += 2;
                                            responseData.setMaxCallee(ByteUtil.byteToUShort(data, index, ByteOrder.AB));
                                            index += 2;
                                            responseData.setPduLength(ByteUtil.byteToUShort(data, index, ByteOrder.AB));
                                            break;
                                        case READ:
                                            int item = data[index++] & 0xff;
                                            while (item > 0) {
                                                S7Enum.ReturnCode returnCode = S7Enum.ReturnCode.valueOf(data[index++]);
                                                S7DataItem dataItem;
                                                if (returnCode == S7Enum.ReturnCode.SUCCESS) {
                                                    dataItem = new S7DataItem(returnCode,
                                                            S7Enum.TransportSize.valueOf(data[index++]),
                                                            ByteUtil.byteToUShort(data, index, ByteOrder.AB));
                                                    index += 2;
                                                    byte[] bytes = new byte[dataItem.getBitAmount() / 8];
                                                    System.arraycopy(data, index, bytes, 0, bytes.length);
                                                    index += bytes.length;
                                                    dataItem.setData(bytes);
                                                } else {
                                                    dataItem = new S7DataItem(returnCode);
                                                    index += 3;
                                                }
                                                item--;
                                                responseData.addItem(dataItem);
                                            }
                                            break;
                                        case WRITE:
                                            item = data[index++] & 0xff;
                                            while (item > 0) {
                                                S7DataItem dataItem = new S7DataItem(S7Enum.ReturnCode.valueOf(data[index++]));
                                                responseData.addItem(dataItem);
                                                item--;
                                            }
                                            break;
                                    }
                                }
                                break;
                        }
                        this.responseData = responseData;
                    }
                    break;
            }
        }
        return size - begin - tpktSize;
    }

    public static void main(String[] args) {
        byte[] bytes = ByteUtil.hexStringToByte("0300001902F080320300000000000200040000040103000000");
        S7MessageAnalysis analysis = new S7MessageAnalysis();
        analysis.fill(bytes, bytes.length,"");
        analysis.gain();
    }
}
